/*
  GREP_AdjustFontSizeByDelta.jsx

  功能：
  - 列出系统中可用的 InDesign GREP 预设（Find/Change 查询 - GREP）。
  - 允许输入一个增量 X（pt，可正可负，支持小数）。
  - 对活动文档中该 GREP 预设查询到的所有文本的字号（pointSize, pt）加上 X。

  使用方法：
  1) 打开一个文档。
  2) 运行本脚本，选择一个 GREP 预设并输入调整值 X。
  3) 确认后对匹配文本进行批量调整。

  说明：
  - 本脚本尝试从应用程序和用户配置目录中收集 GREP 预设（.xml 文件）。
  - 若未找到预设，将提示先在 InDesign 中保存 GREP 预设。
  - 若文本存在混排字号，将逐个样式范围/字符进行加法调整。
  - 为避免非法值，结果字号小于 0.1 pt 时将被限制到 0.1 pt。
*/

(function () {
  if (app.documents.length === 0) {
    alert('请先打开一个文档。');
    return;
  }

  // 列出可用的 GREP 查询预设名（文件名去扩展名）
  var queryNames = listGrepQueryNames();
  if (!queryNames || queryNames.length === 0) {
    alert('未找到任何 GREP 预设。\n请先在 InDesign 中保存 GREP 查询预设（Find/Change > GREP）。');
    return;
  }

  // 简单 UI：选择 GREP 预设 + 输入 X（pt）
  var ui = buildUI(queryNames);
  if (ui.canceled) return;
  var queryName = ui.queryName;
  var delta = ui.delta;

  // 执行：加载预设并查找匹配，逐个范围调整字号
  var doc = app.activeDocument;
  var oldRedraw = app.scriptPreferences.enableRedraw;
  app.scriptPreferences.enableRedraw = false;

  // 重置并加载 GREP 查询
  app.findGrepPreferences = NothingEnum.NOTHING;
  app.changeGrepPreferences = NothingEnum.NOTHING;

  try {
    app.loadFindChangeQuery(queryName, SearchModes.grepSearch);
  } catch (e) {
    app.scriptPreferences.enableRedraw = oldRedraw;
    alert('加载 GREP 预设失败：' + e);
    return;
  }

  var matches;
  try {
    matches = doc.findGrep();
  } catch (e2) {
    app.scriptPreferences.enableRedraw = oldRedraw;
    alert('执行 GREP 查找失败：' + e2);
    return;
  } finally {
    app.findGrepPreferences = NothingEnum.NOTHING;
    app.changeGrepPreferences = NothingEnum.NOTHING;
  }

  if (!matches || matches.length === 0) {
    app.scriptPreferences.enableRedraw = oldRedraw;
    alert('未找到匹配文本。');
    return;
  }

  // 批量修改放到一个撤销组内
  app.doScript(function () {
    var changedRanges = 0;
    for (var i = 0; i < matches.length; i++) {
      changedRanges += adjustRangePointSize(matches[i], delta);
    }
  }, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, '按 GREP 预设调整字号');

  app.scriptPreferences.enableRedraw = oldRedraw;
  alert('处理完成。匹配范围：' + matches.length + '。');

  // ------ 内部函数 ------

  function buildUI(names) {
    var w = new Window('dialog', 'GREP 预设字号批量调整');
    w.orientation = 'column';
    w.alignChildren = ['fill', 'top'];

    var g1 = w.add('group');
    g1.add('statictext', undefined, '选择 GREP 预设：');
    var dd = g1.add('dropdownlist', undefined, names);
    dd.minimumSize.width = 320;
    dd.selection = 0;

    var g2 = w.add('group');
    g2.add('statictext', undefined, '输入调整值 X（pt，可正可负，小数可）：');
    var et = g2.add('edittext', undefined, '1');
    et.characters = 14;

    var btns = w.add('group');
    btns.alignment = 'right';
    var okBtn = btns.add('button', undefined, '确定', { name: 'ok' });
    var cancelBtn = btns.add('button', undefined, '取消', { name: 'cancel' });

    var result = { canceled: true, queryName: null, delta: 0 };
    okBtn.onClick = function () {
      var sel = dd.selection ? dd.selection.text : null;
      if (!sel) {
        alert('请选择一个 GREP 预设。');
        return;
      }
      var v = String(et.text).replace(',', '.');
      var num = parseFloat(v);
      if (!isFinite(num)) {
        alert('请输入有效数字（例如 -1.5 或 2）。');
        return;
      }
      result.canceled = false;
      result.queryName = sel;
      result.delta = num;
      w.close(1);
    };
    cancelBtn.onClick = function () { w.close(0); };

    var shown = w.show();
    if (shown !== 1) {
      result.canceled = true;
    }
    return result;
  }

  // 将一个文本范围的字号整体加上 delta（pt）
  // 返回被处理的样式范围数量（用于统计，可忽略）
  function adjustRangePointSize(txt, delta) {
    var processed = 0;
    try {
      if (!txt || !txt.isValid) return 0;

      // 优先在 textStyleRanges 上处理（统一样式段）
      var ranges = txt.textStyleRanges && txt.textStyleRanges.length > 0
        ? txt.textStyleRanges.everyItem().getElements()
        : null;

      if (ranges && ranges.length > 0) {
        for (var i = 0; i < ranges.length; i++) {
          var ps = ranges[i].pointSize;
          if (isFiniteNumber(ps)) {
            ranges[i].pointSize = clampPt(ps + delta);
            processed++;
          } else {
            // 存在混排，退回到逐字符
            processed += adjustCharactersPointSize(ranges[i].characters, delta);
          }
        }
      } else {
        // 没有样式段（罕见），退回到逐字符
        processed += adjustCharactersPointSize(txt.characters, delta);
      }
    } catch (_) {
      // 忽略单个范围的异常，尽量继续
    }
    return processed;
  }

  function adjustCharactersPointSize(chars, delta) {
    var processed = 0;
    try {
      if (!chars || chars.length === 0) return 0;
      var arr = chars.everyItem().getElements();
      for (var i = 0; i < arr.length; i++) {
        var ps = arr[i].pointSize;
        if (isFiniteNumber(ps)) {
          arr[i].pointSize = clampPt(ps + delta);
          processed++;
        }
      }
    } catch (_) { }
    return processed;
  }

  function clampPt(val) {
    return val < 0.1 ? 0.1 : val; // 避免无效/负值字号
  }

  function isFiniteNumber(v) {
    return typeof v === 'number' && isFinite(v);
  }

  // 收集 GREP 查询预设名（.xml 文件名去扩展名）
  function listGrepQueryNames() {
    var map = {};
    function addFolderQueries(folder) {
      try {
        if (!folder || !folder.exists) return;
        var files = folder.getFiles(function (f) {
          return f instanceof File && /\.xml$/i.test(f.name);
        });
        for (var i = 0; i < files.length; i++) {
          var name = files[i].displayName.replace(/\.xml$/i, '');
          if (name) map[name] = true;
        }
      } catch (_) { }
    }

    // 应用程序目录（不同安装结构下的常见位置）
    var appFolder = app.filePath;
    var appCandidates = [
      Folder(appFolder + '/Presets/Find-Change Queries/Grep'),
      Folder(appFolder + '/Presets/Find-Change Queries/GREP'),
      Folder(appFolder + '/Resources/Find-Change Queries/Grep'),
      Folder(appFolder + '/Resources/Find-Change Queries/GREP')
    ];
    for (var i = 0; i < appCandidates.length; i++) addFolderQueries(appCandidates[i]);

    // 用户数据目录（不同系统/本地化下的常见路径）
    var ud = Folder.userData;
    var userRoots = [
      Folder(ud + '/Adobe InDesign'),
      Folder(ud + '/Adobe/InDesign')
    ];
    for (var r = 0; r < userRoots.length; r++) {
      var root = userRoots[r];
      if (!root.exists) continue;
      var versions = root.getFiles(function (f) { return f instanceof Folder && /^Version/i.test(f.name); });
      for (var v = 0; v < versions.length; v++) {
        var locs = versions[v].getFiles(function (f) { return f instanceof Folder; });
        for (var l = 0; l < locs.length; l++) {
          addFolderQueries(Folder(locs[l].fsName + '/Find-Change Queries/Grep'));
          addFolderQueries(Folder(locs[l].fsName + '/Find-Change Queries/GREP'));
        }
      }
    }

    var names = [];
    for (var k in map) if (map.hasOwnProperty(k)) names.push(k);
    names.sort();
    return names;
  }
})();

